home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-10-24 | 18.1 KB | 694 lines | [TEXT/CWIE] |
- // ===========================================================================
- // LBalloonTracker.cp ©1996 CS&T Inc. All rights reserved.
- // ===========================================================================
- //
- // LBalloonTracker implements Balloon Help for PowerPlant view hierarchies.
-
- // system headers
- #include <Balloons.h>
- #include <Resources.h>
-
- // PowerPlant headers
- #include <LControl.h>
- #include <LString.h>
- #include <LView.h>
- #include <PP_Constants.h>
- #include <UException.h>
- #include <UMemoryMgr.h>
- #include <UResourceMgr.h>
-
- // project headers
- #include "LBalloonTracker.h"
-
-
- // ===========================================================================
- // • LBalloonTracker::Init LBalloonTracker::Init •
- // ===========================================================================
- //
- // This class is used to initialise the LBalloonTracker class at static
- // initialisation time. Its principal task is to determine if Balloon Help
- // is available.
-
- #pragma mark ••• LBalloonTracker::Init •••
-
- class LBalloonTracker::Init
- {
- Init();
- ~Init();
- };
-
-
- LBalloonTracker::Init::Init()
- {
- long result;
- OSErr err;
-
- err = ::Gestalt(gestaltHelpMgrAttr, &result);
- LBalloonTracker::sHasBalloonHelp = ((err == noErr) &&
- (result & (1L << gestaltHelpMgrPresent)));
-
- LBalloonTracker::ClearCache();
- }
-
-
- LBalloonTracker::Init::~Init()
- {
- // there's nothing to do
- }
-
-
- // ===========================================================================
- // • LBalloonTracker::Loader LBalloonTracker::Loader •
- // ===========================================================================
- //
- // This is a stack-based class for loading an LBalloonTracker's balloon help
- // resource ('PPbl'). The constructor (1) saves the current resource file;
- // (2) makes the tracker's resource file current; (3) loads the resource
- // if it's not already there; and (4) locks it. The destructor reverses
- // these steps.
-
- #pragma mark -
- #pragma mark ••• LBalloonTracker::Loader •••
-
- class LBalloonTracker::Loader
- {
- public:
- Loader(const LBalloonTracker& inTracker);
- ~Loader();
- private:
- Int16 mSavedFile;
- Handle mHandle;
- SInt8 mState;
- };
-
-
- // ---------------------------------------------------------------------------
- // • Loader
- // ---------------------------------------------------------------------------
- // Constructor.
-
- LBalloonTracker::Loader::Loader(const LBalloonTracker& inTracker)
- : mSavedFile(::CurResFile()), mHandle(NULL), mState(0)
- {
- // Check if the given balloon tracker has valid info in it. At this
- // point, the current resource file has been saved into mSavedFile.
-
- if (inTracker.HasBalloonFile() &&
- inTracker.HasBalloonDataID() &&
- (inTracker.mBalloonData != NULL))
- {
- // We have valid info
-
- LBalloonTracker::BalloonDef** balloonData = inTracker.mBalloonData;
-
- mHandle = reinterpret_cast<Handle>(balloonData);
-
- // Set the current resource file to the one belonging to inTracker
- ::UseResFile(inTracker.mBalloonFile);
-
- // Load the balloon help resource if it's not already in memory
-
- if (*mHandle == NULL)
- {
- ::LoadResource(mHandle);
- if (::ResError() != noErr)
- mHandle = NULL;
- }
-
- // If we managed to load the resource, save its memory state and lock it
-
- if (mHandle != NULL)
- {
- mState = ::HGetState(mHandle);
- ::HLock(mHandle);
- }
- }
- }
-
-
- // ---------------------------------------------------------------------------
- // • ~Loader
- // ---------------------------------------------------------------------------
- // Destructor. If the balloon help resource was loaded, its memory state
- // (purged, locked, etc) is restored. The resource file that was current at
- // the time of construction is restored also.
-
- LBalloonTracker::Loader::~Loader()
- {
- if (mHandle != NULL)
- ::HSetState(mHandle, mState);
-
- ::UseResFile(mSavedFile);
- }
-
-
- // ===========================================================================
- // • LBalloonTracker LBalloonTracker •
- // ===========================================================================
- #pragma mark -
- #pragma mark ••• LBalloonTracker •••
-
-
- // The resource type of our balloon help
- static const ResType rPPBalloonData = 'PPbl';
-
- // The structure of our balloon help
-
- typedef struct {
- Uint16 length;
- PaneIDT paneID;
- Uint16 type;
- union {
- Uint8 pstrs[1];
- ResIDT picts[4];
- ResIDT teres[4];
- ResIDT strs[4];
- HMStringResType stres[4];
- } u;
- } OneBalloon;
-
- typedef union BalloonDataOrChar {
- OneBalloon b;
- char c[1];
- } BalloonDataOrChar;
-
- struct LBalloonTracker::BalloonDef {
- Uint16 vers;
- Uint32 options;
- Uint16 procid;
- Uint16 variant;
- Uint16 count;
- BalloonDataOrChar elem[1];
- };
-
- // static member variables
- Int16 LBalloonTracker::sDefaultResFile = ::CurResFile();
- Boolean LBalloonTracker::sHasBalloonHelp;
- LPane* LBalloonTracker::sLastBalloonPane;
- LBalloonTracker* LBalloonTracker::sLastBalloonTracker;
- Rect LBalloonTracker::sLastHelpRect;
- Int16 LBalloonTracker::sLastHelpIndex;
- Int16 LBalloonTracker::sLastHelpOptions;
- Int16 LBalloonTracker::sLastHelpProcID;
- Int16 LBalloonTracker::sLastHelpVariant;
- Point LBalloonTracker::sLastHelpTip;
- LBalloonTracker::Init LBalloonTracker::sInit;
-
-
- // ---------------------------------------------------------------------------
- // • SetDefaultBalloonFile [static]
- // ---------------------------------------------------------------------------
- // Sets the resource file to be used in all subsequently created balloon
- // trackers. By default, the balloon help resources ('PPbl') are looked for
- // in the application's resource file. Call this function if you want to
- // look in an other file instead (eg, if your balloon help is in a separate
- // file).
-
- void
- LBalloonTracker::SetDefaultBalloonFile(Int16 inBalloonResFile)
- {
- sDefaultResFile = inBalloonResFile;
- }
-
-
- // ---------------------------------------------------------------------------
- // • LBalloonTracker()
- // ---------------------------------------------------------------------------
- // Default constructor. The balloon tracker is disabled (because it doesn't
- // have a balloon help resource ID). It can subseqently be enabled by a call
- // to SetBalloonsID().
-
- LBalloonTracker::LBalloonTracker()
- : mBalloonDataID(0), mBalloonFile(sDefaultResFile),
- mBalloonData(NULL)
- {
- }
-
-
- // ---------------------------------------------------------------------------
- // • LBalloonTracker(ResIDT inBalloonsID)
- // ---------------------------------------------------------------------------
- // Constructor. The balloon tracker will look for a balloon help resource
- // with an ID of inBalloonsID in the default balloon help resource file.
-
- LBalloonTracker::LBalloonTracker(ResIDT inBalloonsID)
- : mBalloonDataID(0), mBalloonFile(sDefaultResFile),
- mBalloonData(NULL)
- {
- SetBalloonDataID(inBalloonsID);
- }
-
-
- // ---------------------------------------------------------------------------
- // • ~LBalloonTracker
- // ---------------------------------------------------------------------------
- // Destructor. Remove any visible balloon if it belongs to this balloon
- // tracker.
-
- LBalloonTracker::~LBalloonTracker()
- {
- RemoveBalloonsIfInTracker();
- }
-
-
- // ---------------------------------------------------------------------------
- // • SetBalloonsID
- // ---------------------------------------------------------------------------
- // Changes the resource ID of the balloon help resource in which this balloon
- // tracker will look for balloon help.
-
- void
- LBalloonTracker::SetBalloonDataID(ResIDT inBalloonDataID)
- {
- RemoveBalloonsIfInTracker();
-
- mBalloonDataID = inBalloonDataID;
- mBalloonData = NULL;
-
- if (IsBalloonHelpAvailable() && HasBalloonFile() && HasBalloonDataID())
- {
- // don't load the balloon data into memory
- StResLoad dontLoadData;
- Handle rsrcHand;
- Int16 savedFile;
-
- savedFile = ::CurResFile();
- ::UseResFile(mBalloonFile);
- rsrcHand = ::Get1Resource(rPPBalloonData, mBalloonDataID);
- ::UseResFile(savedFile);
-
- // If we can't load the balloon help, it's no big deal, so we
- // simply ignore the error and assign NULL to mBalloonData.
-
- mBalloonData = reinterpret_cast<BalloonDef **>(rsrcHand);
- }
- }
-
-
- // ---------------------------------------------------------------------------
- // • GetBalloonID
- // ---------------------------------------------------------------------------
- // Given an LPane and a point in port coordinates, this function returns
- // (1) the pane's ID; (2) its balloon help index; (3) its hot rect; and (4)
- // its tip location. Derived classes can override this function to allow
- // Balloon Help text to vary within a single pane.
-
- void
- LBalloonTracker::GetBalloonID(
- LPane* inHitPane,
- Point /* inPortPt */,
- PaneIDT& outPaneID,
- Int16& outHelpIndex,
- Rect& outHotRect,
- Point& outTipPoint) const
- {
- Assert_(inHitPane != NULL);
-
- LView* superView = inHitPane->GetSuperView();
- PaneIDT paneID;
- Int16 helpIndex;
- Rect hotRect;
- Point tipPt;
-
- // Get pane's ID
-
- paneID = inHitPane->GetPaneID();
-
- // Get help index. The index dictates which help message will be
- // displayed for the given pane, and depends on the pane's state.
- // For ordinary panes, the "enabled" or "disabled" message is
- // displayed, depending on the pane's enabled state. For
- // descendents of LControl, the "enabled" message is displayed if
- // the control's value is 0, the "checked" message is displayed
- // if the control's value is 1, and the "other" message is
- // displayed for any other value.
-
- #if __option(RTTI)
-
- // we have RTTI -- use pane value for LControls
-
- if (inHitPane->IsEnabled())
- {
- LControl* theHitControl = dynamic_cast<LControl *>(inHitPane);
-
- if (theHitControl != NULL)
- {
- Int32 paneValue = inHitPane->GetValue();
-
- if (paneValue == 0)
- helpIndex = kHMEnabledItem;
- else if (paneValue == 1)
- helpIndex = kHMCheckedItem;
- else
- helpIndex = kHMOtherItem;
- }
- else
- helpIndex = kHMEnabledItem;
- }
- else
- helpIndex = kHMDisabledItem;
-
- #else // __option(RTTI)
-
- // no RTTI -- we can only rely on enabled state
- helpIndex = inHitPane->IsEnabled() ? kHMEnabledItem : kHMDisabledItem;
-
- #endif // __option(RTTI)
-
- // Get pane's "hot" (i.e., frame) rect (in global coordinates). If the
- // pane has a superview, we return the intersection of the pane's frame
- // and its superview's revealed rect.
-
- inHitPane->CalcPortFrameRect(hotRect);
-
- if (superView != NULL)
- {
- Rect revealedRect;
-
- superView->GetRevealedRect(revealedRect);
- ::SectRect(&revealedRect, &hotRect, &hotRect);
- }
-
- inHitPane->PortToGlobalPoint(topLeft(hotRect));
- inHitPane->PortToGlobalPoint(botRight(hotRect));
-
- // Get pane's tip location (also in global coordinates).
- // We place the tip in the bottom right corner of the pane's
- // hot rectangle. If this rectangle is small, we place the tip
- // in the center of the pane instead.
-
- if (hotRect.right - hotRect.left >= 16)
- tipPt.h = hotRect.right - 8;
- else
- tipPt.h = (hotRect.right + hotRect.left) / 2;
-
- if (hotRect.bottom - hotRect.top >= 16)
- tipPt.v = hotRect.bottom - 8;
- else
- tipPt.v = (hotRect.bottom + hotRect.top) / 2;
-
- // return all of the values we've computed
- outPaneID = paneID;
- outHelpIndex = helpIndex;
- outHotRect = hotRect;
- outTipPoint = tipPt;
- }
-
-
- // ---------------------------------------------------------------------------
- // • TrackBalloons
- // ---------------------------------------------------------------------------
- // This function tracks the mouse and displays the help balloons for panes
- // belonging to inHitView's pane hierarchy. InGlobalPt and inPortPt give
- // the mouse location in global and port coordinates, respectively.
-
- void
- LBalloonTracker::TrackBalloons(
- LView* inHitView,
- Point inPortPt)
- {
- LPane* theHitPane = NULL;
- HMMessageRecord helpMsg;
- Int16 theHitIndex, helpOptions, helpProcID, helpVariant;
- Rect theHotRect;
- Point theTipPoint;
- Boolean foundHelp, paneChanged, indexChanged, rectChanged;
- Boolean removeIfNotFound, dataChanged;
- OSErr err;
-
- Assert_(inHitView != NULL);
-
- // do we have Balloon Help ?
- if (!IsBalloonHelpAvailable())
- return;
-
- // is Balloon Help turned on ?
- if (!::HMGetBalloons())
- {
- // it's turned off -- clear cached variables
-
- if (sLastBalloonPane != NULL)
- ClearCache();
-
- return;
- }
-
- // do we have balloon data ? if not, just return.
- if (!HasBalloonFile() || !HasBalloonDataID())
- {
- return;
- }
-
- // does the given view contain the mouse location ?
- if (!inHitView->Contains(inPortPt.h, inPortPt.v))
- {
- // the view doesn't contain the mouse -- ignore
- return;
- }
-
- // locate the deepest Pane that contains the mouse location
- theHitPane = inHitView->FindDeepSubPaneContaining(inPortPt.h, inPortPt.v);
- if (theHitPane == NULL)
- theHitPane = inHitView;
-
- removeIfNotFound = true;
-
- // locate the deepest Pane that has balloon data
- for (foundHelp = false;
- theHitPane != NULL;
- theHitPane = theHitPane->GetSuperView())
- {
- PaneIDT theHitID;
-
- GetBalloonID(theHitPane, inPortPt, theHitID, theHitIndex,
- theHotRect, theTipPoint);
-
- // check for "special" pane IDs -- they indicate that we should ignore
- // this pane.
-
- if ((theHitID == PaneIDT_Undefined) || (theHitID == PaneIDT_Unspecified))
- {
- if (theHitID == PaneIDT_Unspecified)
- removeIfNotFound = false;
- break;
- }
-
- foundHelp = FindBalloon(theHitID, theHitIndex, helpMsg, helpOptions,
- helpProcID, helpVariant);
-
- if (foundHelp)
- break;
- }
-
- // did we find a pane with balloon data ?
- if (!foundHelp)
- {
- // no pane was found
-
- if (removeIfNotFound)
- {
- // remove any currently displayed balloon
-
- if (::HMIsBalloon())
- {
- err = ::HMRemoveBalloon();
- ThrowIfOSErr_(err);
- }
-
- if (sLastBalloonPane != NULL)
- ClearCache();
- }
-
- return;
- }
-
- paneChanged = (theHitPane != sLastBalloonPane);
- indexChanged = (theHitIndex != sLastHelpIndex);
- rectChanged = !::EqualRect(&theHotRect, &sLastHelpRect);
- dataChanged = (paneChanged || indexChanged || rectChanged);
-
- if (!dataChanged && ::HMIsBalloon())
- {
- // the new data is still valid -- we don't need to do anything
- return;
- }
-
- if (dataChanged && ::HMIsBalloon())
- {
- // we have new data, but there's already a balloon displayed;
- // remove it
-
- err = ::HMRemoveBalloon();
- ThrowIfOSErr_(err);
- }
-
- // if we're displaying help for the same pane as the old one, place
- // the tip at the old location
- if (!paneChanged && !rectChanged)
- {
- theTipPoint = sLastHelpTip;
- }
-
- Rect savedLastHelpRect;
-
- savedLastHelpRect = sLastHelpRect;
- sLastHelpRect = theHotRect;
-
- // display the new balloon
- err = ::HMShowBalloon(&helpMsg, theTipPoint, &sLastHelpRect, NULL,
- helpProcID, helpVariant, helpOptions);
-
- if (err == noErr)
- {
- // cache the new info
- sLastBalloonPane = theHitPane;
- sLastBalloonTracker = this;
- sLastHelpIndex = theHitIndex;
- sLastHelpOptions = helpOptions;
- sLastHelpProcID = helpProcID;
- sLastHelpVariant = helpVariant;
- sLastHelpTip = theTipPoint;
- }
- else if ((err != hmHelpDisabled) && (err != hmBalloonAborted))
- {
- sLastHelpRect = savedLastHelpRect;
-
- ThrowOSErr_(err);
- }
- }
-
-
- // ---------------------------------------------------------------------------
- // • RemoveBalloons
- // ---------------------------------------------------------------------------
- // Removes any currently displayed help balloon.
-
- void
- LBalloonTracker::RemoveBalloons()
- {
- OSErr err;
-
- if (::HMGetBalloons() && ::HMIsBalloon())
- {
- // A balloon is currently visible -- remove it
-
- err = ::HMRemoveBalloon();
- ThrowIfOSErr_(err);
- }
-
- ClearCache();
- }
-
-
- // ---------------------------------------------------------------------------
- // • RemoveBalloonsIfInTracker
- // ---------------------------------------------------------------------------
- // Removes any currently displayed help balloon if the currently cached
- // balloon tracker is "this".
-
- void
- LBalloonTracker::RemoveBalloonsIfInTracker()
- {
- if (this == sLastBalloonTracker)
- RemoveBalloons();
- }
-
-
- // ---------------------------------------------------------------------------
- // • FindBalloon
- // ---------------------------------------------------------------------------
- // Given a Pane ID and a help message index, returns the help message text
- // and other info necessary to display a help balloon. Returns true if the
- // given pane's help info was found and valid; else, returns false.
-
- Boolean
- LBalloonTracker::FindBalloon(
- PaneIDT inPaneID,
- Int16 inHelpIndex,
- HMMessageRecord& outHelpMsg,
- Int16& outHelpOptions,
- Int16& outHelpProcID,
- Int16& outHelpVariant) const
- {
- Loader loadHelp(*this);
- BalloonDef* balloons;
- BalloonDataOrChar* bdoc;
- Boolean found, goodData;
-
- if (!HasBalloonData())
- return (false);
-
- balloons = *mBalloonData;
- bdoc = balloons->elem;
- found = false;
-
- // loop through the help resource, looking for our pane ID
- for (Uint16 i = 0; i < balloons->count; i++)
- {
- if (bdoc->b.paneID == inPaneID)
- {
- found = true;
- break;
- }
-
- bdoc = (BalloonDataOrChar *) &bdoc->c[bdoc->b.length];
- }
-
- if (!found)
- return (false);
-
- outHelpOptions = (balloons->options & (hmSaveBitsNoWindow | hmSaveBitsWindow)) >> 2;
- outHelpProcID = balloons->procid;
- outHelpVariant = balloons->variant;
-
- // fill in the help message record
- switch (bdoc->b.type)
- {
- case kHMStringItem:
- {
- ConstStringPtr pStr = bdoc->b.u.pstrs;
-
- while (inHelpIndex-- > 0)
- pStr = pStr + StrLength(pStr) + 1;
-
- LString::CopyPStr(pStr, outHelpMsg.u.hmmString);
-
- outHelpMsg.hmmHelpType = khmmString;
- goodData = (StrLength(outHelpMsg.u.hmmString) > 0);
- }
- break;
-
- case kHMPictItem:
- outHelpMsg.hmmHelpType = khmmPict;
- outHelpMsg.u.hmmPict = bdoc->b.u.picts[inHelpIndex];
- goodData = (outHelpMsg.u.hmmPict != 0);
- break;
-
- case kHMStringResItem:
- outHelpMsg.hmmHelpType = khmmStringRes;
- outHelpMsg.u.hmmStringRes = bdoc->b.u.stres[inHelpIndex];
- goodData = ((outHelpMsg.u.hmmStringRes.hmmResID != 0) &&
- (outHelpMsg.u.hmmStringRes.hmmIndex > 0));
- break;
-
- case kHMTEResItem:
- outHelpMsg.hmmHelpType = khmmTERes;
- outHelpMsg.u.hmmTERes = bdoc->b.u.teres[inHelpIndex];
- goodData = (outHelpMsg.u.hmmTERes != 0);
- break;
-
- case kHMSTRResItem:
- outHelpMsg.hmmHelpType = khmmSTRRes;
- outHelpMsg.u.hmmSTRRes = bdoc->b.u.strs[inHelpIndex];
- goodData = (outHelpMsg.u.hmmSTRRes != 0);
- break;
-
- default:
- SignalPStr_("\pUndefined help data type!");
- goodData = false;
- break;
- }
-
- return (found && goodData);
- }
-